/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package downloadmanager;

import downloadmanager.component.MultiSegmentProgressBar;
import downloadmanager.component.MultiSegmentProgressBarRenderer;
import downloadmanager.component.XMenu;
import downloadmanager.component.XMenuItem;
import downloadmanager.downloader.Downloader;
import downloadmanager.downloader.DownloaderBean;
import downloadmanager.downloader.DownloaderState;
import downloadmanager.event.XEventListener;
import java.awt.*;
import java.awt.event.*;
import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.TableModel;
import util.ClipboardExtensions;
import util.FileExtensions;

// The Download Manager.
public class DownloadManagerFrame extends JFrame {

    public final int DEFAULT_THREAD_COUNT = 8;
    private final String DOWNLOAD_LIST_FILE = "downloads.xml";
    private DownloadsTableModel downloadTableModel;
    private JTable downloadTable;
    private JButton btnPause, btnResume, btnRemove, btnLaunchFile, btnLaunchDirectory, btnCopyURL;
    private Downloader selectedDownloader;
    private boolean clearing;
    private XEventListener<DownloaderState> downloadStateChanged = new XEventListener<DownloaderState>() {

        public void eventFired(Object sender, DownloaderState args) {
            Downloader downloader = (Downloader) sender;
            if (downloader == selectedDownloader) {
                updateButtons();
            }

        }
    };

    // Constructor for Download Manager.
    public DownloadManagerFrame() {
        setTitle("Download Manager");
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        setSize(640, 480);
        setJMenuBar(getPreparedMenuBar());

        downloadTableModel = new DownloadsTableModel();
        downloadTable = getDownloadTable(downloadTableModel);

        // Set up downloads panel.
        JPanel downloadsPanel = new JPanel();
        downloadsPanel.setBorder(
                BorderFactory.createTitledBorder("Downloads"));
        downloadsPanel.setLayout(new BorderLayout());
        downloadsPanel.add(new JScrollPane(downloadTable), BorderLayout.CENTER);

        // Set up buttons panel.
        JPanel buttonsPanel = new JPanel();
        initiateButtons(buttonsPanel);

        // Add panels to display.
        getContentPane().setLayout(new BorderLayout());
        getContentPane().add(downloadsPanel, BorderLayout.CENTER);
        getContentPane().add(buttonsPanel, BorderLayout.SOUTH);
        updateButtons();
        try {
            loadDownloads();
        } catch (FileNotFoundException ex) {
            Logger.getLogger(DownloadManagerFrame.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private JMenuBar getPreparedMenuBar() {
        JMenuBar menuBar = new JMenuBar();
        JMenu fileMenu = new XMenu("File", KeyEvent.VK_F,
                new XMenuItem("Add new download", KeyEvent.VK_A,
                new ActionListener() {

                    public void actionPerformed(ActionEvent e) {
                        addNewDownload();
                    }
                }),
                new XMenuItem("Exit", KeyEvent.VK_X,
                new ActionListener() {

                    public void actionPerformed(ActionEvent e) {
                        exit();
                    }
                }));

        menuBar.add(fileMenu);
        return menuBar;
    }

    private JTable getDownloadTable(TableModel tableModel) {
        JTable table = new JTable(tableModel);
        table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {

            public void valueChanged(ListSelectionEvent e) {
                tableSelectionChanged();
            }
        });

        table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        table.setDefaultRenderer(MultiSegmentProgressBar.class, new MultiSegmentProgressBarRenderer());
        return table;
    }

    private void initiateButtons(JPanel container) {
        ActionListener buttonActionListener = new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                ManipulateCommand cmd = ManipulateCommand.None;
                if (e.getSource() == btnPause) {
                    cmd = ManipulateCommand.Pause;
                } else if (e.getSource() == btnResume) {
                    cmd = ManipulateCommand.Resume;
                } else if (e.getSource() == btnRemove) {
                    cmd = ManipulateCommand.Remove;
                } else if (e.getSource() == btnLaunchFile) {
                    cmd = ManipulateCommand.OpenFile;
                } else if (e.getSource() == btnLaunchDirectory) {
                    cmd = ManipulateCommand.OpenDirectory;
                } else if (e.getSource() == btnCopyURL) {
                    cmd = ManipulateCommand.CopyURL;
                }
                try {
                    processCurrentDownload(cmd);
                } catch (Exception ex) {
                    Logger.getLogger(DownloadManagerFrame.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        };
        JButton[] buttons = {btnPause = new JButton("Pause"), btnResume = new JButton("Resume"), btnRemove = new JButton("Remove"),
            btnLaunchFile = new JButton("Open File"), btnLaunchDirectory = new JButton("Open Directory"), btnCopyURL = new JButton("Copy URL")};
        for (JButton b : buttons) {
            container.add(b);
            b.addActionListener(buttonActionListener);
        }
    }

    // Exit this program.
    private void exit() {
        setVisible(false);
        dispose();
    }

    // Add a new download.
    private void addNewDownload() {
        DownloadJob job = NewDownloadDialog.getDownloadJob();
        if (job != null) {
            try {
                Downloader d = new Downloader(new URL(job.getUrl()), job.getLocalFile(), DEFAULT_THREAD_COUNT);
                downloadTableModel.addDownload(d);
                d.start();
            } catch (MalformedURLException ex) {
                Logger.getLogger(DownloadManagerFrame.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    // Called when table row selection changes.
    private void tableSelectionChanged() {
        if (selectedDownloader != null) {
            selectedDownloader.StateChanged.unsubscribe(downloadStateChanged);
        }

        if (downloadTable.getSelectedRow() != -1) {
            selectedDownloader = downloadTableModel.getDownload(downloadTable.getSelectedRow());
        } else {
            selectedDownloader = null;
        }

        if (selectedDownloader != null) {
            selectedDownloader.StateChanged.subscribe(downloadStateChanged);
        }

        updateButtons();
    }

    private void processCurrentDownload(ManipulateCommand cmd) throws InterruptedException, IOException {
        if (selectedDownloader != null) {
            switch (cmd) {
                case None:
                    return;
                case Pause:
                    selectedDownloader.pause();
                    break;
                case Resume:
                    selectedDownloader.start();
                    break;
                case Remove:
                    selectedDownloader.pause();
                    downloadTableModel.removeDownload(selectedDownloader);
                    break;
                case OpenFile:
                    FileExtensions.launchFile(selectedDownloader.getLocalFile());
                    return;
                case OpenDirectory:
                    FileExtensions.launchDirectory(selectedDownloader.getLocalFile());
                    return;
                case CopyURL:
                    ClipboardExtensions.setText(selectedDownloader.getURL().toString());
                    return;

            }
            updateButtons();
        }
    }


    /* Update each button's state based off of the
    currently selected download's status. */
    private void updateButtons() {
        if (selectedDownloader != null) {
            btnCopyURL.setEnabled(true);
            btnRemove.setEnabled(true);
            btnLaunchFile.setEnabled(false);
            btnLaunchDirectory.setEnabled(true);
            DownloaderState status = selectedDownloader.getState();
            switch (status) {
                case Working:
                    btnPause.setEnabled(true);
                    btnResume.setEnabled(false);
                    break;

                case Ended:
                    btnPause.setEnabled(false);
                    btnResume.setEnabled(false);
                    btnLaunchFile.setEnabled(true);
                    break;

                case EndedWithError:
                case Paused:
                case Prepared:
                    btnPause.setEnabled(false);
                    btnResume.setEnabled(true);
                    break;
                default:
                    btnPause.setEnabled(false);
                    btnResume.setEnabled(false);
                    break;
            }

        } else {
            // No download is selected in table.
            btnPause.setEnabled(false);
            btnResume.setEnabled(false);
            btnRemove.setEnabled(false);
            btnLaunchFile.setEnabled(false);
            btnLaunchDirectory.setEnabled(false);
            btnCopyURL.setEnabled(false);
        }

    }

    private void pauseAllDownloads() throws InterruptedException {
        java.util.List<Downloader> downloads = this.downloadTableModel.getDownloadList();
        for (Downloader d : downloads) {
            d.pause();
        }
    }

    private void saveDownloads() throws FileNotFoundException, IOException {
        FileOutputStream f = new FileOutputStream(DOWNLOAD_LIST_FILE);
        XMLEncoder xe = new XMLEncoder(f);
        java.util.List<Downloader> downloads = this.downloadTableModel.getDownloadList();
        for (Downloader d : downloads) {
            xe.writeObject(new DownloaderBean(d));
        }
        xe.close();
        f.close();
    }

    private void loadDownloads() throws FileNotFoundException {
        File f = new File(DOWNLOAD_LIST_FILE);
        if (f.exists()) {
            FileInputStream fi = new FileInputStream(f);
            XMLDecoder xd = new XMLDecoder(fi);
            DownloaderBean d = null;
            do {
                try {
                    d = (DownloaderBean) xd.readObject();
                    this.downloadTableModel.addDownload(d.toDownloader());
                } catch (ArrayIndexOutOfBoundsException ex) {
                    d = null;
                    return;
                }
            } while (true);
        }
    }

    @Override
    public void dispose() {
        try {
            pauseAllDownloads();
        } catch (InterruptedException ex) {
            Logger.getLogger(DownloadManagerFrame.class.getName()).log(Level.SEVERE, null, ex);
        }

        try {
            saveDownloads();
        } catch (FileNotFoundException ex) {
            Logger.getLogger(DownloadManagerFrame.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(DownloadManagerFrame.class.getName()).log(Level.SEVERE, null, ex);
        }
        super.dispose();
    }

    // Run the Download Manager.
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            public void run() {
                DownloadManagerFrame manager = new DownloadManagerFrame();
                manager.setVisible(true);
            }
        });
    }
}
